Skip to content

fix(hosting): chain entry-assembly and registered XAML metadata providers#144

Merged
codemonkeychris merged 2 commits into
mainfrom
fix/142-host-app-xaml-metadata-provider
May 5, 2026
Merged

fix(hosting): chain entry-assembly and registered XAML metadata providers#144
codemonkeychris merged 2 commits into
mainfrom
fix/142-host-app-xaml-metadata-provider

Conversation

@codemonkeychris
Copy link
Copy Markdown
Collaborator

Summary

Closes #142.

WinUI's lifted XAML loader resolves local: namespaces and Generic.xaml type/property references through Application.Current's IXamlMetadataProvider chain. ReactorApplication previously only delegated to Reactor's own generated provider plus a hand-written core stub, so any custom Control declared in the consuming app — or in a referenced third-party library when the consumer has no XAML of its own — went unresolved, and the lifted parser took the process down with Failed to create a 'Microsoft.UI.Xaml.DependencyProperty' from the text 'MyText' before the control could render.

The bug title talks about "readonly dependency properties" but the failure surfaces for any user-defined property whose declaring type isn't reachable through Application.Current's metadata chain — the customer's repro just happened to reduce to a control with one private DP.

What changed

1. Entry-assembly auto-discovery (src/Reactor/Hosting/ReactorApp.cs)

ReactorApplication now scans the entry assembly for any concrete, parameterless-ctor IXamlMetadataProvider (the type the WinUI XAML compiler emits when a project has any XAML file) and chains it between Reactor's own provider and the core stub. Skips Reactor's own generated type to avoid recursion if the entry assembly happens to be Reactor under test hosting. Covers the common case — when the consumer has any XAML file, the compiler-generated OtherProviders list transitively chains every referenced library, so this single discovery brings the whole graph in.

2. Explicit registration API for 3P libraries

For the no-XAML-consumer case, the compiler-generated chain doesn't exist, so referenced libraries can't be reached transitively. Added:

public static void ReactorApp.RegisterControlAssembly(IXamlMetadataProvider provider);
public static void ReactorApp.RegisterControlAssembly(System.Reflection.Assembly assembly);

Idempotent, thread-safe, copy-on-write snapshot semantics so the hot lookup path on the UI thread needs no locking. The Assembly overload reflects out the compiler-generated provider type and instantiates it. Mirrors the Win2D / CommunityToolkit setup pattern in pure WinUI.

// In a no-XAML Reactor app referencing a 3P control library:
ReactorApp.RegisterControlAssembly(typeof(SomeThirdPartyControl).Assembly);
ReactorApp.Run<MyComponent>("My App");

3. Tests

  • Issue142_CustomControlPrivateDp_Renders — entry-assembly path. Custom control + Generic.xaml in Reactor.AppTests.Host itself, exercises the auto-discovery.
  • Issue142_ThirdPartyControlPrivateDp_Renders — registration path. New standalone library tests/Reactor.AppTests.ThirdPartyControls/ simulates a 3P NuGet (custom control with private DP + Themes/Generic.xaml + {TemplateBinding MyText}). Fixture calls RegisterControlAssembly(...) and asserts both the registration list and the rendered template-bound text.

The 3P fixture's failure mode (process crash without registration in a no-XAML consumer) was verified manually with a temporary throwaway app; the fixture itself passes either way in this test host because the host's own Themes/Generic.xaml triggers the auto-chain. The fixture comment explains this and asserts the registered-provider list is non-empty so the API is at least exercised.

Test plan

  • dotnet build Reactor.sln -c Debug — clean.
  • Reactor.AppTests.Host.exe --self-test — 2214/2214 ok.
  • dotnet test tests/Reactor.Tests — 6793/0 fail.
  • Manually verified no-XAML consumer scenario: control renders with RegisterControlAssembly, process crashes without it (matches issue [Bug] Controls with readonly dependency properties are not supported #142 originate error).

🤖 Generated with Claude Code

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes WinUI lifted-XAML metadata resolution for consumer-defined and third-party custom controls by extending Application.Current’s IXamlMetadataProvider chain in ReactorApplication, addressing issue #142.

Changes:

  • Added entry-assembly discovery of the consuming app’s compiler-generated IXamlMetadataProvider and chained it into ReactorApplication’s lookup path.
  • Introduced ReactorApp.RegisterControlAssembly(...) APIs to explicitly register third-party metadata providers (with copy-on-write, thread-safe semantics).
  • Added self-test fixtures and a new “third-party controls” test library to validate both the entry-assembly and explicit-registration paths.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Reactor/Hosting/ReactorApp.cs Adds provider auto-discovery + explicit registration APIs and extends the IXamlMetadataProvider chain used by ReactorApplication.
tests/Reactor.AppTests.Host/SelfTest/Fixtures/Issue142Fixtures.cs Adds regression fixtures covering host-assembly and third-party assembly scenarios.
tests/Reactor.AppTests.Host/SelfTest/SelfTestFixtureRegistry.cs Registers the new issue #142 fixtures in the self-test registry.
tests/Reactor.AppTests.Host/Themes/Generic.xaml Adds a host-side Generic.xaml style to exercise entry-assembly provider discovery.
tests/Reactor.AppTests.Host/Reactor.AppTests.Host.csproj References the new third-party control library for the fixture.
tests/Reactor.AppTests.ThirdPartyControls/Reactor.AppTests.ThirdPartyControls.csproj New WinUI class library project to simulate a third-party control package.
tests/Reactor.AppTests.ThirdPartyControls/ThirdPartyControlWithPrivateDp.cs New custom control with a private DP to reproduce the metadata resolution failure mode.
tests/Reactor.AppTests.ThirdPartyControls/Themes/Generic.xaml New third-party Generic.xaml applying a template using {TemplateBinding MyText}.
Reactor.sln Adds the new third-party controls test project to the solution.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Reactor/Hosting/ReactorApp.cs Outdated
codemonkeychris and others added 2 commits May 4, 2026 20:23
…ders

WinUI's lifted XAML loader resolves `local:` namespaces and Generic.xaml
type/property references through `Application.Current`'s
`IXamlMetadataProvider` chain. `ReactorApplication` previously only
delegated to Reactor's own generated provider plus a hand-written core
stub, so any custom `Control` declared in the consuming app — or in a
referenced third-party library when the consumer has no XAML of its
own — went unresolved, and the lifted parser took the process down with
a `Failed to create a 'Microsoft.UI.Xaml.DependencyProperty' from the
text 'MyText'` originate error before the control could render.

Two-part fix:

1. Auto-discover the entry assembly's XAML-compiler-generated
   `XamlMetaDataProvider` and chain it between Reactor's provider and the
   core stub. Covers the common case where the consumer has any XAML
   file (which transitively chains every referenced library through the
   compiler-generated `OtherProviders` list).

2. Add `ReactorApp.RegisterControlAssembly(IXamlMetadataProvider)` and
   `RegisterControlAssembly(Assembly)` for the no-XAML-consumer case,
   where there is no compiler-generated chain to ride. Idempotent,
   thread-safe, copy-on-write snapshot semantics so the hot lookup path
   needs no locking. Mirrors the documented Win2D / CommunityToolkit
   pattern in pure WinUI apps.

Selftests: `Issue142_CustomControlPrivateDp_Renders` exercises path 1 via
a control declared in the host project; `Issue142_ThirdPartyControlPrivateDp_Renders`
exercises path 2 via the new `Reactor.AppTests.ThirdPartyControls`
library that simulates a real 3P control NuGet.

Fixes #142

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses #144 review feedback. `ex.Types` is `Type?[]`, so the previous
`.Where(t => t is not null).ToArray()!` pattern relied on a nullable
suppression and left a redundant `t is null` check inside the scan loop.
`OfType<Type>().ToArray()` produces a true `Type[]`, eliminating both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codemonkeychris codemonkeychris force-pushed the fix/142-host-app-xaml-metadata-provider branch from 20f1016 to d5dc991 Compare May 5, 2026 03:27
@codemonkeychris codemonkeychris merged commit 7983633 into main May 5, 2026
6 checks passed
@codemonkeychris codemonkeychris deleted the fix/142-host-app-xaml-metadata-provider branch May 5, 2026 03:47
@dotMorten
Copy link
Copy Markdown
Collaborator

Verified :)
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Controls with readonly dependency properties are not supported

3 participants